﻿/*----------------------------------------------------------------
// 公司名称：公司名称
// 
// 产品名称：Acl.UnitTest
//
// 创 建 人：王青松
// 创建日期：2022-12-07
// 邮    箱：wangqingsong@163.com
//
// Copyright (C) 公司名称 2009，保留所有权利
//
//----------------------------------------------------------------*/
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace Acl.UnitTest.Annotions
{
    /// <summary>
    /// 测试类
    /// </summary>
    public class TestClass
    {
        /// <summary>
        /// 
        /// </summary>
        public string 类名 { get; private set; }


        public TestCaseInfo[] UseCases { get; private set; } = Array.Empty<TestCaseInfo>();

        static string GetMethodName(string name)
        {
            //方法名解析
            const string strTest = "Test";
            var methodName = name.AsSpan();
            var index = methodName.IndexOf(strTest.AsSpan());

#if NET6_0_OR_GREATER
            return index > 0
                 ? new string(methodName.Slice(0, index).TrimEnd('_'))
                  : name;
#elif NET452_OR_GREATER
            return index > 0
                ? new string(methodName.Slice(0, index).TrimEnd('_').ToArray())
                 : name;
#endif
        }

        static string GetUseCaseDescription(Attribute[] atts)
        {
            //测试用例描述解析
            const string DescriptionAttribute = nameof(DescriptionAttribute);
            var att = atts.FirstOrDefault(x => x.GetType().Name.AsSpan().SequenceEqual(DescriptionAttribute.AsSpan()));
            if (att == null) return default;

            return att is System.ComponentModel.DescriptionAttribute desAtt
                ? desAtt.Description
                : GetNUnitDescriptionValue(att);
        }

        static string GetNUnitDescriptionValue(Attribute att)
        {
            var prop = att.GetType().GetTypeInfo().GetRuntimeProperty("Properties");
            if (prop == null) return default;
            var proBag = prop.GetValue(att, null);
            if (proBag == null) return default;

            var method = proBag.GetType().GetMethod("Get");
            if (method == null) return default;

            var value = method.Invoke(proBag, new object[] { "Description" });
            if (value == null) return default;
            return (string)value;
        }

        const string nunitTestFixture = "TestFixtureAttribute";
        const string msTestFixture = "TestClassAttribute";


        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool ValidateTestClassAttributeName(string name)
        {
            var nameSpan = name.AsSpan();
            return nameSpan.SequenceEqual(nunitTestFixture.AsSpan()) || nameSpan.SequenceEqual(msTestFixture.AsSpan());
        }

        const string nunitTestMethod = "TestAttribute";
        const string msTestMethod = "TestMethodAttribute";

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        static bool ValidateTestMethodName(string name)
        {
            var nameSpan = name.AsSpan();
            return nameSpan.SequenceEqual(nunitTestMethod.AsSpan()) || nameSpan.SequenceEqual(msTestMethod.AsSpan());
        }

        static string GetPrecondition(Attribute[] atts)
        {
            var att = atts.OfType<PreconditionAttribute>().FirstOrDefault();
            return att?.Description;
        }

        public static TestClass[] Scan(Assembly asm)
        {
          
         

            var result = (from t in asm.GetTypes()
                          let className = t.GetTypeInfo().Name
                          let isTestClass = t.GetCustomAttributes().Any(x => ValidateTestClassAttributeName(x.GetType().Name))
                          where isTestClass
                          from method in t.GetMethods()
                          let methodAtts = method.GetCustomAttributes().ToArray()
                          where methodAtts.Length > 0 && methodAtts.Any(x => ValidateTestMethodName(x.GetType().Name))
                          select (className, method, methodAtts))
                         .GroupBy(x => x.className)
                         .Select(x => new TestClass
                         {
                             类名 = x.Key,
                             UseCases = x.Select((y, i) => new TestCaseInfo
                             {
                                 序号 = i + 1,
                                 类名 = y.className,
                                 方法名 = GetMethodName(y.method.Name),
                                 用例名称 = y.method.Name,
                                 测试用例描述 = GetUseCaseDescription(y.methodAtts),
                                 前置条件 = GetPrecondition(y.methodAtts),
                                 输入 = y.methodAtts.OfType<InputAttribute>().FirstOrDefault()?.Description,
                                 预期结果 = y.methodAtts.OfType<ExpectedResultAttribute>().FirstOrDefault()?.Description,
                                 备注 = y.methodAtts.OfType<CommentAttribute>().FirstOrDefault()?.Description,
                             }).ToArray()
                         }).ToArray();

            return result;
        }

    }
}




